/**
 * @brief 通过epoll实现对描述符的 IO 事件监控
 * @details
 * 1.支持添加/删除/修改事件
 * 2.Poll通过epoll_wait返回所有有就绪的事件的描述符之后返回对应的channel
 */
#pragma once

#include <sys/epoll.h>
#include "channel.hpp"

namespace hdp
{
#define MAX_EPOLLEVENT 1024
    class Poller
    {
    private:
        int _epfd;
        struct epoll_event _evs[MAX_EPOLLEVENT];
        std::unordered_map<int, Channel *> _channels; // 文件描述符和channel的映射关系

    private:
        // 对epoll的直接操作
        void Update(Channel *channel, int op)
        {
            // int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
            struct epoll_event ev;
            ev.events = channel->Events();
            ev.data.fd = channel->Fd();

            int ret = epoll_ctl(_epfd, op, channel->Fd(), &ev);
            if (ret < 0)
            {
                LOG_ERROR("epollctl failed");
            }
            return;
        }

        // 判断一个channel是否已经添加了事件监控
        bool HasChannel(Channel *Channel)
        {
            auto it = _channels.find(Channel->Fd());
            return it != _channels.end();

            // if(it == _channels.end()) return false;
            // return true;
        }

    public:
        Poller()
        {
            // int epoll_create(int size);
            _epfd = epoll_create(MAX_EPOLLEVENT);
            if (_epfd < 0)
            {
                LOG_ERROR("epoll create failed");
                abort();
            }
        }

        // 添加或修改监控事件
        void UpdateEvent(Channel *channel)
        {
            bool ret = HasChannel(channel);
            // 不存在则添加
            if (ret == false)
            {
                _channels.insert(std::make_pair(channel->Fd(), channel));
                return Update(channel, EPOLL_CTL_ADD);
            }
            // 存在则修改
            return Update(channel, EPOLL_CTL_MOD);
        }

        // 移除监控
        void RemoveEvent(Channel *channel)
        {
            // bool ret = HasChannel(channel);
            // if(ret != false)
            // {
            //     _channels.erase(channel->Fd());
            //     return Update(channel,EPOLL_CTL_DEL);
            // }

            auto it = _channels.find(channel->Fd());
            if (it != _channels.end())
            {
                _channels.erase(it);
                return Update(channel, EPOLL_CTL_DEL);
            }
        }

        // 开始监控,返回活跃连接
        void Poll(std::vector<Channel *> *actives)
        {
            // int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
            int nfds = epoll_wait(_epfd, _evs, MAX_EPOLLEVENT, -1);
            if (nfds < 0)
            {
                if (errno == EINTR)
                {
                    return;
                }
                LOG_ERROR("epoll wait failed");
                abort();
            }

            for (int i = 0; i < nfds; i++)
            {
                auto it = _channels.find(_evs[i].data.fd);
                assert(it != _channels.end());

                // 设置实际的就绪事件
                it->second->SetRevents(_evs[i].events);
                actives->push_back(it->second);
            }
            return;
        }
    };
}